/*
 * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package javax.security.auth;

import java.security.Security;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.util.Objects;
import sun.security.util.Debug;

/**
 * <p> This is an abstract class for representing the system policy for
 * Subject-based authorization.  A subclass implementation
 * of this class provides a means to specify a Subject-based
 * access control {@code Policy}.
 *
 * <p> A {@code Policy} object can be queried for the set of
 * Permissions granted to code running as a
 * {@code Principal} in the following manner:
 *
 * <pre>
 *      policy = Policy.getPolicy();
 *      PermissionCollection perms = policy.getPermissions(subject,
 *                                                      codeSource);
 * </pre>
 *
 * The {@code Policy} object consults the local policy and returns
 * and appropriate {@code Permissions} object with the
 * Permissions granted to the Principals associated with the
 * provided <i>subject</i>, and granted to the code specified
 * by the provided <i>codeSource</i>.
 *
 * <p> A {@code Policy} contains the following information.
 * Note that this example only represents the syntax for the default
 * {@code Policy} implementation. Subclass implementations of this class
 * may implement alternative syntaxes and may retrieve the
 * {@code Policy} from any source such as files, databases,
 * or servers.
 *
 * <p> Each entry in the {@code Policy} is represented as
 * a <b><i>grant</i></b> entry.  Each <b><i>grant</i></b> entry
 * specifies a codebase, code signers, and Principals triplet,
 * as well as the Permissions granted to that triplet.
 *
 * <pre>
 *      grant CodeBase ["URL"], Signedby ["signers"],
 *            Principal [Principal_Class] "Principal_Name" {
 *          Permission Permission_Class ["Target_Name"]
 *                                      [, "Permission_Actions"]
 *                                      [, signedBy "SignerName"];
 *      };
 * </pre>
 *
 * The CodeBase and Signedby components of the triplet name/value pairs
 * are optional.  If they are not present, then any any codebase will match,
 * and any signer (including unsigned code) will match.
 * For Example,
 *
 * <pre>
 *      grant CodeBase "foo.com", Signedby "foo",
 *            Principal com.sun.security.auth.SolarisPrincipal "duke" {
 *          permission java.io.FilePermission "/home/duke", "read, write";
 *      };
 * </pre>
 *
 * This <b><i>grant</i></b> entry specifies that code from "foo.com",
 * signed by "foo', and running as a {@code SolarisPrincipal} with the
 * name, duke, has one {@code Permission}.  This {@code Permission}
 * permits the executing code to read and write files in the directory,
 * "/home/duke".
 *
 * <p> To "run" as a particular {@code Principal},
 * code invokes the {@code Subject.doAs(subject, ...)} method.
 * After invoking that method, the code runs as all the Principals
 * associated with the specified {@code Subject}.
 * Note that this {@code Policy} (and the Permissions
 * granted in this {@code Policy}) only become effective
 * after the call to {@code Subject.doAs} has occurred.
 *
 * <p> Multiple Principals may be listed within one <b><i>grant</i></b> entry.
 * All the Principals in the grant entry must be associated with
 * the {@code Subject} provided to {@code Subject.doAs}
 * for that {@code Subject} to be granted the specified Permissions.
 *
 * <pre>
 *      grant Principal com.sun.security.auth.SolarisPrincipal "duke",
 *            Principal com.sun.security.auth.SolarisNumericUserPrincipal "0" {
 *          permission java.io.FilePermission "/home/duke", "read, write";
 *          permission java.net.SocketPermission "duke.com", "connect";
 *      };
 * </pre>
 *
 * This entry grants any code running as both "duke" and "0"
 * permission to read and write files in duke's home directory,
 * as well as permission to make socket connections to "duke.com".
 *
 * <p> Note that non Principal-based grant entries are not permitted
 * in this {@code Policy}.  Therefore, grant entries such as:
 *
 * <pre>
 *      grant CodeBase "foo.com", Signedby "foo" {
 *          permission java.io.FilePermission "/tmp/scratch", "read, write";
 *      };
 * </pre>
 *
 * are rejected.  Such permission must be listed in the
 * {@code java.security.Policy}.
 *
 * <p> The default {@code Policy} implementation can be changed by
 * setting the value of the {@code auth.policy.provider} security property to
 * the fully qualified name of the desired {@code Policy} implementation class.
 *
 * @see java.security.Security security properties
 * @deprecated as of JDK version 1.4 -- Replaced by java.security.Policy. java.security.Policy has a
 * method:
 * <pre>
 *      public PermissionCollection getPermissions
 *          (java.security.ProtectionDomain pd)
 *
 * </pre>
 * and ProtectionDomain has a constructor:
 * <pre>
 *      public ProtectionDomain
 *          (CodeSource cs,
 *           PermissionCollection permissions,
 *           ClassLoader loader,
 *           Principal[] principals)
 * </pre>
 *
 * These two APIs provide callers the means to query the Policy for Principal-based Permission
 * entries.
 */
@Deprecated
public abstract class Policy {

  private static Policy policy;
  private final static String AUTH_POLICY =
      "sun.security.provider.AuthPolicyFile";

  private final java.security.AccessControlContext acc =
      java.security.AccessController.getContext();

  // true if a custom (not AUTH_POLICY) system-wide policy object is set
  private static boolean isCustomPolicy;

  /**
   * Sole constructor.  (For invocation by subclass constructors, typically
   * implicit.)
   */
  protected Policy() {
  }

  /**
   * Returns the installed Policy object.
   * This method first calls
   * {@code SecurityManager.checkPermission} with the
   * {@code AuthPermission("getPolicy")} permission
   * to ensure the caller has permission to get the Policy object.
   *
   * <p>
   *
   * @return the installed Policy.  The return value cannot be {@code null}.
   * @throws java.lang.SecurityException if the current thread does not have permission to get the
   * Policy object.
   * @see #setPolicy
   */
  public static Policy getPolicy() {
    java.lang.SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
      sm.checkPermission(new AuthPermission("getPolicy"));
    }
    return getPolicyNoCheck();
  }

  /**
   * Returns the installed Policy object, skipping the security check.
   *
   * @return the installed Policy.
   */
  static Policy getPolicyNoCheck() {
    if (policy == null) {

      synchronized (Policy.class) {

        if (policy == null) {
          String policy_class = null;
          policy_class = AccessController.doPrivileged
              (new PrivilegedAction<String>() {
                public String run() {
                  return java.security.Security.getProperty
                      ("auth.policy.provider");
                }
              });
          if (policy_class == null) {
            policy_class = AUTH_POLICY;
          }

          try {
            final String finalClass = policy_class;

            Policy untrustedImpl = AccessController.doPrivileged(
                new PrivilegedExceptionAction<Policy>() {
                  public Policy run() throws ClassNotFoundException,
                      InstantiationException,
                      IllegalAccessException {
                    Class<? extends Policy> implClass = Class.forName(
                        finalClass, false,
                        Thread.currentThread().getContextClassLoader()
                    ).asSubclass(Policy.class);
                    return implClass.newInstance();
                  }
                });
            AccessController.doPrivileged(
                new PrivilegedExceptionAction<Void>() {
                  public Void run() {
                    setPolicy(untrustedImpl);
                    isCustomPolicy = !finalClass.equals(AUTH_POLICY);
                    return null;
                  }
                }, Objects.requireNonNull(untrustedImpl.acc)
            );
          } catch (Exception e) {
            throw new SecurityException
                (sun.security.util.ResourcesMgr.getString
                    ("unable.to.instantiate.Subject.based.policy"));
          }
        }
      }
    }
    return policy;
  }


  /**
   * Sets the system-wide Policy object. This method first calls
   * {@code SecurityManager.checkPermission} with the
   * {@code AuthPermission("setPolicy")}
   * permission to ensure the caller has permission to set the Policy.
   *
   * <p>
   *
   * @param policy the new system Policy object.
   * @throws java.lang.SecurityException if the current thread does not have permission to set the
   * Policy.
   * @see #getPolicy
   */
  public static void setPolicy(Policy policy) {
    java.lang.SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
      sm.checkPermission(new AuthPermission("setPolicy"));
    }
    Policy.policy = policy;
    // all non-null policy objects are assumed to be custom
    isCustomPolicy = policy != null ? true : false;
  }

  /**
   * Returns true if a custom (not AUTH_POLICY) system-wide policy object
   * has been set or installed. This method is called by
   * SubjectDomainCombiner to provide backwards compatibility for
   * developers that provide their own javax.security.auth.Policy
   * implementations.
   *
   * @return true if a custom (not AUTH_POLICY) system-wide policy object has been set; false
   * otherwise
   */
  static boolean isCustomPolicySet(Debug debug) {
    if (policy != null) {
      if (debug != null && isCustomPolicy) {
        debug.println("Providing backwards compatibility for " +
            "javax.security.auth.policy implementation: " +
            policy.toString());
      }
      return isCustomPolicy;
    }
    // check if custom policy has been set using auth.policy.provider prop
    String policyClass = java.security.AccessController.doPrivileged
        (new java.security.PrivilegedAction<String>() {
          public String run() {
            return Security.getProperty("auth.policy.provider");
          }
        });
    if (policyClass != null && !policyClass.equals(AUTH_POLICY)) {
      if (debug != null) {
        debug.println("Providing backwards compatibility for " +
            "javax.security.auth.policy implementation: " +
            policyClass);
      }
      return true;
    }
    return false;
  }

  /**
   * Retrieve the Permissions granted to the Principals associated with
   * the specified {@code CodeSource}.
   *
   * <p>
   *
   * @param subject the {@code Subject} whose associated Principals, in conjunction with the
   * provided {@code CodeSource}, determines the Permissions returned by this method.  This
   * parameter may be {@code null}. <p>
   * @param cs the code specified by its {@code CodeSource} that determines, in conjunction with the
   * provided {@code Subject}, the Permissions returned by this method.  This parameter may be
   * {@code null}.
   * @return the Collection of Permissions granted to all the {@code Subject} and code specified in
   * the provided <i>subject</i> and <i>cs</i> parameters.
   */
  public abstract java.security.PermissionCollection getPermissions
  (Subject subject,
      java.security.CodeSource cs);

  /**
   * Refresh and reload the Policy.
   *
   * <p>This method causes this object to refresh/reload its current
   * Policy. This is implementation-dependent.
   * For example, if the Policy object is stored in
   * a file, calling {@code refresh} will cause the file to be re-read.
   *
   * <p>
   *
   * @throws SecurityException if the caller does not have permission to refresh the Policy.
   */
  public abstract void refresh();
}
